/*
 * Copyright 2008,2009,2010 Daniel Freitas
 * 
 * This file is part of DMF Generic DAO.
 * 
 * DMF Generic DAO is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General
 * Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 * 
 * DMF Generic DAO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with DMF Generic DAO. If not, see
 * <http://www.gnu.org/licenses/>.
 */
package com.googlecode.dmf.genericdao.spring.scanners;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An abstract package scanner which implements the template method design pattern. The method
 * {@link AbstractBasePackageScanner#getClassNames(URI, String)} must be implemented by children classes to provide the
 * template method with the list of class names to get {@link Class} information from.
 * 
 * @author <a href="mailto:daniel.mfreitas@gmail.com">Daniel Freitas</a>
 */
public abstract class AbstractBasePackageScanner implements IPackageScanner {

    /**
     * A utility logger available to children classes. The logger will be initialized to the class name.
     */
    protected final Logger logger = LoggerFactory.getLogger(getClass());

    /**
     * 
     * {@inheritDoc}
     * 
     * This is a template method. Children classes complete the algorithm by providing an implementation to both the
     * {@link IPackageScanner#extractResourceContainerUri(URI, String)} and
     * {@link AbstractBasePackageScanner#getClassNames(URI, String)} methods. Refer to the Javadoc of both methods for
     * more information on how to implement them.
     * 
     * @see com.googlecode.dmf.genericdao.spring.scanners.IPackageScanner#scan(java.net.URI, java.lang.String)
     */
    public List<Class<?>> scan(final URI resourceUri, final String basePackage) throws IOException, URISyntaxException {
        URI rootPath = extractResourceContainerUri(resourceUri, basePackage);
        List<String> classNames = getClassNames(rootPath, basePackage);

        return classesForNames(classNames);
    }

    /**
     * Given a root path, returns the names of all classes under the root path in which the package name includes the
     * provided base package. Classes in subpackages of the base package must also be included.
     * 
     * @param rootPath
     *            A URI to the path where to look for the class files.
     * @param basePackage
     *            The package to be used as the start point. The resulting list must include files contained in
     *            subpackages of this base package as well.
     * @return A list of strings denoting the class names under the target package and any subpackages of the target
     *         package.
     * 
     * @throws IOException
     *             If an error occur while performing the required I/O operations to retrieve the file paths.
     */
    protected abstract List<String> getClassNames(final URI rootPath, final String basePackage) throws IOException;

    private List<Class<?>> classesForNames(final List<String> classNames) {

        List<Class<?>> result = new ArrayList<Class<?>>();

        for (String className : classNames) {
            try {
                Class<?> classResource = Class.forName(className, true, Thread.currentThread().getContextClassLoader());

                result.add(classResource);
            } catch (ClassNotFoundException e) {
                logger.warn(MessageFormat.format("A Class object could not be obtained from file {0}.", className));
            }
        }

        return result;
    }

}